#ifndef __tenacitas_command__processor__h__
#define __tenacitas_command__processor__h__


// =============================================================================
/** @file */


// =============================================================================
//                                 C++ Headers
#include <memory>
#include <iostream>

// =============================================================================
//                                 3rds Headers


// =============================================================================
//                                 Our Headers


// =============================================================================
//                                 Namespaces
using namespace std;


// =============================================================================
//                                 Macro Contants
/** */


// =============================================================================
//                                 Macro Commands
/** */


/** namespace of the organization */
namespace tenacitas {
    /** namespace for the project */
    namespace command {

        // =========================== Typedefs ===========================
        /** */

        // ============================ Pre-Declarations ==================

        // ============================ Attributes ========================
        /** */

        // ============================ Functions =========================
        /** */

        // ============================ Classes ===========================

        /** Obtains messages from a connection, translates them into inout
            suitable for an application

            @sa Please, see the type parameters documentation at
            <tt>receiver.h</tt> file, associated to
            <tt>tenacitas_command::receiver</tt> class
            @nosubgrouping 
        */
        template <
            typename t_message_types,
            typename t_connection_types,
            typename t_app_data_types,
            typename t_translator_types,
            typename t_application_types,
            typename t_timeout_types,
            typename t_message_io_types
            >
        class processor {

            // --- processor - Friends --------------------------------------

        public:

            // --- processor - Public Internal Types ------------------------

            typedef t_message_types message_types;
            typedef typename message_types::message message;
            typedef typename message_types::message_ptr message_ptr;

            typedef t_app_data_types app_data_types;
            typedef typename app_data_types::app_data app_data;
            typedef typename app_data_types::app_data_ptr app_data_ptr;

            typedef t_connection_types connection_types;
            typedef typename connection_types::connection connection;
            typedef typename connection_types::connection_ptr connection_ptr;

            typedef t_translator_types translator_types;
            typedef typename translator_types::translator_factory translator_factory;
            typedef typename translator_types::translator_factory_ptr
            translator_factory_ptr; 
            typedef typename translator_types::translator translator;
            typedef typename translator_types::translator_ptr translator_ptr;

            typedef t_application_types application_types;
            typedef typename application_types::application_factory application_factory;
            typedef typename application_types::application_factory_ptr
            application_factory_ptr; 
            typedef typename application_types::application application;
            typedef typename application_types::application_ptr application_ptr;

            typedef t_timeout_types timeout_types;
            typedef typename timeout_types::timeout_factory timeout_factory;
            typedef typename timeout_types::timeout_factory_ptr
            timeout_factory_ptr; 
            typedef typename timeout_types::timeout timeout;
            typedef typename timeout_types::timeout_ptr timeout_ptr;

            typedef t_message_io_types message_io_types;
            typedef typename message_io_types::message_io_factory message_io_factory;
            typedef typename message_io_types::message_io_factory_ptr
            message_io_factory_ptr; 
            typedef typename message_io_types::message_io message_io;
            typedef typename message_io_types::message_io_ptr message_io_ptr;

            // --- processor - Public Constructors --------------------------

            /** */
            processor( connection_ptr p_con,
                       translator_factory_ptr p_translator_factory,
                       application_factory_ptr p_application_factory,
                       timeout_factory_ptr p_timeout_factory,
                       message_io_factory_ptr p_message_io_factory )
                : m_con( p_con ),
                  m_translator_factory( p_translator_factory ),
                  m_application_factory( p_application_factory ),
                  m_timeout_factory( p_timeout_factory ),
                  m_message_io_factory( p_message_io_factory ) {}

            processor( ) = delete;

            processor( const processor & p_processor )
                : m_con( p_processor.m_con ){}
            

            /** Destructor */
            ~processor( ) {}

            // --- processor - Public Operators -----------------------------

            /** Process incoming messages */
            void operator( )( ) {

                // object that will read a message to be handled
                message_io_ptr l_message_io = m_message_io_factory->create( );

                if ( !l_message_io ) {
                    // @todo: log a critical error and throw an exception
                    std::cout << "No message io available" << std::endl;
                    return;
                }
                    
                // object that will handle the incoming message
                application_ptr l_application = m_application_factory->create( );

                if ( !l_application ) {
                    // @todo: log a critical error and throw an exception
                    std::cout << "No application available" << std::endl;
                    return;
                }

                // object that will translate a message into an app_data suitable
                // for an application
                translator_ptr l_translator = m_translator_factory->create( );

                if ( !l_translator ) {
                    // @todo: log a critical error and throw an exception
                    std::cout << "No translator available" << std::endl;
                    return;
                }
                
                while ( true ) {

                    std::cout << "Waiting for message" << std::endl;
                    
                    message_ptr l_message = l_message_io->read( m_con );
                    if ( !l_message ) {
                        // @todo: log info that no data was available
                        std::cout << "No more data to be read" << std::endl;
                        break;
                    }
                    else {
                        std::cout << "Message '" << *l_message << "' was read";
                    }
                    
                    app_data_ptr l_app_data =
                        l_translator->to_app_data( l_message );
                        
                    ( *l_application )( l_app_data, m_con, l_message_io );
                }
            }

            // --- processor - Public Operators -----------------------------

            /** Assignment operator */
            processor & operator=( const processor & p_processor ) {
                if ( this != & p_processor ) {
                    m_con = p_processor.m_con;
                }
                return *this;
            }

        private:

            // --- processor - Private Attributes ---------------------------

            /** Connection from where messages will arrive, and to where
                messages will be sent */
            connection_ptr m_con;

            translator_factory_ptr m_translator_factory;
            application_factory_ptr m_application_factory;
            timeout_factory_ptr m_timeout_factory;
            message_io_factory_ptr m_message_io_factory;
        };
    }
}

// =============================================================================
//                     I M P L E M E N T A T I O N
// =============================================================================

using namespace tenacitas::command;


#endif
